// #include <sys/select.h>
// #include <iostream>
// #include <cstdio>
// #include <unistd.h>
// #include <fcntl.h>

// void SetNonBlock(int fd)
// {
//     int fl = fcntl(fd, F_GETFD);
//     if (fl < 0)
//     {
//         perror("fcntl");
//         return;
//     }
//     fcntl(fd, F_SETFD, fl | O_NONBLOCK);
// }

// int main()
// {
//     SetNonBlock(0);

//     char buffer[1024];
//     while (true)
//     {
//         ssize_t n = read(0, buffer, sizeof(buffer));

//         if (n > 0)
//         {
//             buffer[n - 1] = 0;
//             std::cout << buffer << std::endl;
//         }
//         else if (n < 0)
//         {
//             // 1. 读取出错 2. 底层没有数据准备好
//             if (errno == EAGAIN || errno == EWOULDBLOCK) // 错误码
//             {
//                 std::cout << "数据没有准备好..." << std::endl;
//                 sleep(1);
//                 // 做你的事情
//                 continue;
//             }
//             else if (errno == EINTR)
//             {
//                 continue;
//             }
//             else
//             {
//                 // 真正的read出错了
//             }
//         }
//         else
//         {
//             break;
//         }

//     }

//     // fd_set fset;

//     // std::cout << sizeof(fd_set) * 8 << std::endl;

//     return 0;
// }

#pragma once
#include <iostream>
#include <unistd.h>
#include <sys/poll.h>
#include <sys/epoll.h>
#include "Socket.hpp"
using namespace SocketModule;

class EpollServer
{
    const static int size = 64;
    const static int defaultfd = -1;

public:
    EpollServer(int port) : _listensock(std::make_unique<TcpSocket>()), _isruning(false), _epfd(defaultfd)
    {
        // 创建listensocket
        _listensock->BuildTcpSocketMethod(port);
        // 2.创建epoll模型
        _epfd = epoll_create(256);
        if (_epfd < 0)
        {
            LOG(LogLevel::FATAL) << "epoll create error ...";
            exit(EPOLL_CREATE_ERROR);
        }
        LOG(LogLevel::INFO) << "epoll create success: " << _epfd; // 必须是4，因为listensocket是3

        // 3.既然有了模型，就要把用户的需要设置到内核，将listensocket设置到内核中
        struct epoll_event ev;
        ev.events = EPOLLIN;
        ev.data.fd = _listensock->Fd(); // 维护的是用户的数据。这里也要添加就是为了，后面的判断是否是新链接还是normol socket
        int n = epoll_ctl(_epfd, EPOLL_CTL_ADD, _listensock->Fd(), &ev);
        if (n < 0)
        {
            LOG(LogLevel::FATAL) << "epoll_ctl add listensocket error";
            exit(EPOLL_CTL_ERROR);
        }
    }

    void Start()
    {
        _isruning = true;
        int timeout = -1; // 阻塞
        while (true)
        {
            int n = epoll_wait(_epfd, revs, size, timeout);
            switch (n)
            {
            case 0:
                LOG(LogLevel::DEBUG) << "timeout...";
                break;
            case -1:
                LOG(LogLevel::DEBUG) << "epoll error";
                break;
            default:
                Dispatcher(n);
                break;
            }
        }
        _isruning = false;
    }
    // 事件派发器
    void Dispatcher(int num)
    {
        LOG(LogLevel::DEBUG) << "event ready...";
        for (int i = 0; i < num; i++)
        {
            // epoll也是需要循环的处理就绪事件，可能有多个fd就绪
            int sockfd = revs[i].data.fd;
            uint32_t revents = revs[i].events;
            if (revents & EPOLLIN)
            {
                // 读事件就绪
                // 跟之前的poll和select一样，处理新链接到来还是normal socket
                if (sockfd == _listensock->Fd())
                {
                    Accepter();
                }
                else
                {
                    Recver(sockfd);
                }
            }
            if (revents & EPOLLOUT)
            {
            }
        }
    }
    // 链接管理器
    void Accepter()
    {
        InetAddr client;
        int sockfd = _listensock->Accept(&client); // 这里一定不会阻塞了，一定有数据，是select告诉的

        if (sockfd >= 0)
        {
            // 获取新链接到来成功
            // 虽然是成功了，但是绝对不能直接进行recv和read，sockfd是否读就绪，我们不知道，还需要select
            // 放进辅助数组就行了
            LOG(LogLevel::INFO) << "get a new link,sockfd: " << sockfd << " , client: " << client.StringAddr();
            struct epoll_event ev;
            ev.data.fd = sockfd;
            ev.events = EPOLLIN;
            int n = epoll_ctl(_epfd, EPOLL_CTL_ADD, sockfd, &ev);
            if (n < 0)
            {
                LOG(LogLevel::WARING) << "epoll_ctl add listensocket error";
            }
            else
            {
                LOG(LogLevel::INFO) << "epoll_ctl add listensocket success: " << sockfd;
            }
        }
    }
    // IO处理器
    void Recver(int sockfd)
    {
        char buffer[1024];
        // 跟上面的同理，一定不会阻塞
        ssize_t n = recv(sockfd, buffer, sizeof(buffer) - 1, 0);
        if (n > 0)
        {
            buffer[n] = 0;
            std::cout << "client say@ " << buffer << std::endl;
        }
        else if (n == 0)
        {
            LOG(LogLevel::INFO) << "client quit...";
            // 不要在让select关心这个文件了
            int m = epoll_ctl(_epfd,EPOLL_CTL_DEL,sockfd,nullptr);
            if(m > 0)
            {
                LOG(LogLevel::INFO) << "epoll_ctl remove sockfd success: " << sockfd;
            }
            close(sockfd);
        }
        else
        {
            LOG(LogLevel::ERROR) << "recv error";
            // 不要在让select关心这个文件了
            int m = epoll_ctl(_epfd,EPOLL_CTL_DEL,sockfd,nullptr);
            if(m > 0)
            {
                LOG(LogLevel::INFO) << "epoll_ctl remove sockfd success: " << sockfd;
            }
            close(sockfd);
        }
    }

    void stop()
    {
        _isruning = false;
    }

    ~EpollServer()
    {
        _listensock->Close();
        if (_epfd > 0)
        {
            close(_epfd);
        }
    }

private:
    std::unique_ptr<Socket> _listensock;
    bool _isruning;

    int _epfd;
    struct epoll_event revs[size];
};